﻿using System;
using System.Linq;
using System.Collections.Generic;
using Topics.Radical.ComponentModel.Messaging;
using Topics.Radical.Validation;
using Raven.Client;
using SampleApp.MvcBackend.ComponentModel;
using SampleApp.MvcBackend.Runtime;

namespace SampleApp.MvcBackend.Services
{
	abstract class AbstractDispatchScheduler<TCommit, TConsumer>
		where TConsumer : DispatchConsumer<TCommit>
		where TCommit : class, ICommit
	{
		readonly IDocumentStore documentSessionFactory;
		readonly IMessageBroker broker;
		TConsumer consumer = null;

		public AbstractDispatchScheduler( IDocumentStore documentSessionFactory, IMessageBroker broker )
		{
			Ensure.That( documentSessionFactory ).Named( () => documentSessionFactory ).IsNotNull();
			Ensure.That( broker ).Named( () => broker ).IsNotNull();

			this.documentSessionFactory = documentSessionFactory;
			this.broker = broker;

			this.TryResume();
		}

		protected virtual void TryResume()
		{
			using ( var session = this.documentSessionFactory.OpenSession() )
			{
				var allPendingCommits = PagingHelper.GetAll( 20, ( pageIndex, pageSize ) =>
				{
					RavenQueryStatistics stats;
					var page = session.Query<TCommit>()
						.Statistics( out stats )
						.Where( c => c.IsDispatched == false )
						.OrderBy( c => c.CreatedOn )
						.Skip( pageSize * pageIndex )
						.Take( pageSize )
						.ToList();

					return new PagingHelper.Results<TCommit>
					{
						Items = page,
						TotalItemsCount = stats.TotalResults
					};
				} );

				if ( allPendingCommits.Any() )
				{
					this.ScheduleDispatch( allPendingCommits );
				}
			}
		}

		public virtual void ScheduleDispatch( IEnumerable<TCommit> commits )
		{
			foreach ( var commit in commits )
			{
				this.ScheduleDispatch( commit );
			}
		}

		protected abstract TConsumer CreateConsumer( IMessageBroker broker, IDocumentStore documentSessionFactory );

		public virtual void ScheduleDispatch( TCommit commit )
		{
			if ( this.consumer == null )
			{
				lock ( this )
				{
					if ( this.consumer == null )
					{
						this.consumer = this.CreateConsumer( this.broker, this.documentSessionFactory );
					}
				}
			}

			this.consumer.ScheduleDispatch( commit );
		}
	}
}
